package com.hk.commons.poi.excel.write.builder;

import com.hk.commons.poi.excel.metadata.WriteWorkbook;
import com.hk.commons.poi.excel.write.handler.SimpleWriteableHandler;
import com.hk.commons.poi.excel.write.handler.WriteableHandler;
import com.hk.commons.util.AssertUtils;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.util.IOUtils;
import org.apache.poi.xssf.streaming.SXSSFWorkbook;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.apache.poi.xssf.usermodel.XSSFWorkbookType;

import java.io.OutputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;


/**
 * @author Kevin
 */
public class ExcelWriteBuilder {

    /**
     * 每一个sheet的 参数
     */
    private final List<WriteWorkbook> writeWorkbooks = new ArrayList<>();

    /**
     * 要写出的位置
     */
    private OutputStream outputStream;

    private Workbook workbook;

    private WriteableHandler writeableHandler;

    public ExcelWriteBuilder(List<?> dataList, Class<?> beanClass) {
        var writeWorkbook = new WriteWorkbook();
        writeWorkbook.setData(dataList);
        writeWorkbook.setBeanClazz(beanClass);
        this.writeWorkbooks.add(writeWorkbook);
    }

    public ExcelWriteBuilder(WriteWorkbook writeWorkbook) {
        this.writeWorkbooks.add(writeWorkbook);
    }

    public ExcelWriteBuilder(List<WriteWorkbook> writeWorkbooks) {
        this.writeWorkbooks.addAll(writeWorkbooks);
    }

    public ExcelWriteBuilder output(OutputStream output) {
        this.outputStream = output;
        return this;
    }

    public ExcelWriteBuilder writeableHandler(WriteableHandler writeableHandler) {
        this.writeableHandler = writeableHandler;
        return this;
    }

    public ExcelWriteBuilder hssf() {
        this.workbook = new HSSFWorkbook();
        return this;
    }

    public XSSFBuilder xssfBuilder() {
        return new XSSFBuilder();
    }

    public SXSSFBuilder sxssfBuilder() {
        return new SXSSFBuilder();
    }

    @NoArgsConstructor(access = AccessLevel.PRIVATE)
    public class XSSFBuilder {

        private XSSFWorkbookType type = XSSFWorkbookType.XLSX;

        public XSSFBuilder type(XSSFWorkbookType type) {
            this.type = type;
            return this;
        }

        public ExcelWriteBuilder builder() {
            workbook = new XSSFWorkbook(type);
            return ExcelWriteBuilder.this;
        }
    }

    @NoArgsConstructor(access = AccessLevel.PRIVATE)
    public class SXSSFBuilder {

        /**
         * <pre>
         * 在内存中保留100行，超过行将刷新到磁盘
         * 如果rowAccessWindowSize <= 0 ,关闭自动刷新并累积内存中的所有行
         * </pre>
         */
        private int rowAccessWindowSize = SXSSFWorkbook.DEFAULT_WINDOW_SIZE;

        /**
         * <pre>
         * 如果是全新的工作表，此参数可以不设置，
         * 如果是在一个已存在的工作表中添加新的工作表，请设置此参数，也可将行追加到现有的工作表
         * </pre>
         */
        private XSSFWorkbook xssfWorkbook;

        /**
         * <pre>
         * 是否压缩临时文件，sxssf写表数据时会写入到临时文件中，这些临时文件大小可能会变成很大
         * 如果设置为true,会使用 gzip压缩文件，
         * 如果设置为true,会有性能上的损失。
         * </pre>
         */
        private boolean compressTmpFiles;

        /**
         *
         */
        private boolean useSharedStringsTable;

        public SXSSFBuilder rowAccessWindowSize(int rowAccessWindowSize) {
            this.rowAccessWindowSize = rowAccessWindowSize;
            return this;
        }

        public SXSSFBuilder xssfWorkbook(XSSFWorkbook xssfWorkbook) {
            this.xssfWorkbook = xssfWorkbook;
            return this;
        }

        public SXSSFBuilder compressTmpFiles(boolean compressTmpFiles) {
            this.compressTmpFiles = compressTmpFiles;
            return this;
        }

        public SXSSFBuilder useSharedStringsTable(boolean useSharedStringsTable) {
            this.useSharedStringsTable = useSharedStringsTable;
            return this;
        }

        public ExcelWriteBuilder builder() {
            workbook = new SXSSFWorkbook(xssfWorkbook, rowAccessWindowSize, compressTmpFiles, useSharedStringsTable);
            return ExcelWriteBuilder.this;
        }
    }

    public void write() {
        AssertUtils.notNull(outputStream, "output must not be null.");
        if (Objects.isNull(workbook)) {
            xssfBuilder().builder();
        }
        if (Objects.isNull(writeableHandler)) {
            writeableHandler = new SimpleWriteableHandler();
        }
        try {
            writeableHandler.write(workbook, writeWorkbooks, outputStream);
        } finally {
            IOUtils.closeQuietly(workbook);
        }
    }
}
